<?php
/**
 * Created by PhpStorm.
 * User: 12133
 * Date: 2025/4/29
 * Time: 9:59
 */

namespace app\service;


class Weixinpay{

    private $mchId; // 商户号
    private $appId; // 应用ID
    private $serialNo; // 证书序列号
    private $privateKey; // 私钥
    private $apiV3Key; // API V3密钥

    public function __construct($mchId, $appId, $serialNo, $privateKey, $apiV3Key)
    {
        $this->mchId = $mchId;
        $this->appId = $appId;
        $this->serialNo = $serialNo;
        $this->privateKey = $privateKey;
        $this->apiV3Key = $apiV3Key;
    }


    //查询
    public function query(array $transferData)
    {
        $url = "https://api.mch.weixin.qq.com/v3/transfer/batches/batch-id";
        $headers = $this->getHeaders($url, json_encode($transferData));

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($transferData));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        curl_close($ch);

        return json_decode($response, true);
    }

    /**
     * 发起商家转账
     * @param array $transferData 转账数据
     * @return array 返回结果
     */
    public function transfer(array $transferData)
    {
        $url = "https://api.mch.weixin.qq.com/v3/transfer/batches";
        $headers = $this->getHeaders($url, json_encode($transferData));

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($transferData));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        curl_close($ch);

        return json_decode($response, true);
    }

    public function transferNew(array $transferData)
    {
        $url = "https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills";
        $headers = $this->getHeaders($url, json_encode($transferData));

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($transferData));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        curl_close($ch);

        return json_decode($response, true);
    }

    public function transferQuery($out_bill_no)
    {
        $url = "https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/$out_bill_no";
        $headers = $this->getHeaders($url, json_encode($transferData));

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
//        curl_setopt($ch, CURLOPT_POST, 1);
//        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($transferData));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        curl_close($ch);

        return json_decode($response, true);
    }

    /**
     * 获取请求头
     * @param string $url 请求URL
     * @param string $body 请求体
     * @return array 请求头数组
     */
    public function getHeaders($url, $body)
    {
        $timestamp = time();
        $nonce = uniqid();
        $message = $this->buildMessage($url, $body, $timestamp, $nonce);
        $signature = $this->sign($message);

        return [
            'Content-Type: application/json',
            'Accept: application/json',
            'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
//            'User-Agent: PHP-WechatPayV3',
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $this->buildAuth($timestamp, $nonce, $signature),
            'Wechatpay-Serial: ' . $this->serialNo,
        ];
    }

    /**
     * 构建签名消息
     * @param string $url 请求URL
     * @param string $body 请求体
     * @param int $timestamp 时间戳
     * @param string $nonce 随机字符串
     * @return string 签名消息
     */
    private function buildMessage($url, $body, $timestamp, $nonce)
    {
        return "POST\n" . parse_url($url, PHP_URL_PATH) . "\n" . $timestamp . "\n" . $nonce . "\n" . $body . "\n";
    }

    /**
     * 签名
     * @param string $message 签名消息
     * @return string 签名
     */
    private function sign($message)
    {
        openssl_sign($message, $signature, $this->privateKey, OPENSSL_ALGO_SHA256);
        return base64_encode($signature);
    }

    /**
     * 构建认证信息
     * @param int $timestamp 时间戳
     * @param string $nonce 随机字符串
     * @param string $signature 签名
     * @return string 认证信息
     */
    private function buildAuth($timestamp, $nonce, $signature)
    {
        return 'mchid="' . $this->mchId . '",nonce_str="' . $nonce . '",timestamp="' . $timestamp . '",serial_no="' . $this->serialNo . '",signature="' . $signature . '"';
    }


    private
    function getEncrypt($str) {
        //$str是待加密字符串
        $public_key_path = '平台证书路径';
        $public_key = file_get_contents($public_key_path);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            throw new Exception('encrypt failed');
        }
        return $sign;
    }

    public
    function decryptToString($associatedData, $nonceStr, $ciphertext)
    {
        $ciphertext = base64_decode($ciphertext);
        if (strlen($ciphertext) <= 16) {
            return false;
        }
// ext-sodium (default installed on >= PHP 7.2)
//        if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && sodium_crypto_aead_aes256gcm_is_available()) {
//            return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->apiV3Key);
//        }
// ext-libsodium (need install libsodium-php 1.x via pecl)
        if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
            return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->apiV3Key);
        }
// openssl (PHP >= 7.1 support AEAD)
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext = substr($ciphertext, 0, -16);
            $authTag = substr($ciphertext, -16);
            return \openssl_decrypt($ctext, 'aes-256-gcm', $this->apiV3Key, \OPENSSL_RAW_DATA, $nonceStr,
                $authTag, $associatedData);
        }
        throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
    }
}